#ifndef __SCHEDULER_H__
#define __SCHEDULER_H__ 

#include<memory>
#include"fiber.h"
#include<vector>
#include<list>


namespace sylar{

class Scheduler{
public:
    using ptr = std::shared_ptr<Scheduler>;
    using MutexType = Mutex;
    /**
     * @brief Construct a new Scheduler object
     * 
     * @param threads 线程数
     * @param use_caller 是否把构造调度器的线程纳入到调度器内
     * @param name 调度器名称
     */
    Scheduler(size_t threads=1, bool use_caller=true, const std::string& name="");

    ~Scheduler();
    const std::string& getName(){return m_name;}

    void start();
    void stop();

    /// 协程一个一个放入
    template<class FiberOrCb>
    void schedule(FiberOrCb fc, int thread=-1){
        bool need_tickle = false;
        {
        MutexType::Lock lock(m_mutex);
        need_tickle = scheduleNoLock(fc, thread);
        }
        /// 需要提醒
        if(need_tickle){
            tickle(); 
        }
    }
    /// 支持批量放入协程队列
    template<class InputIterator>
    void schedule(InputIterator begin, InputIterator end){
        bool need_tickle = false;
        {
            MutexType::Lock lock(m_mutex);
            while(begin!=end){
                need_tickle = scheduleNoLock(&*begin, -1)||need_tickle;
                ++begin;
            }
        }
        if(need_tickle){
            tickle();
        }
    }
    int taskNum(){ return m_fibers.size();}
protected:
    /// 需要提醒
    virtual void tickle();
    /// 执行协程调度的方法
    void run();
    virtual bool stopping();
    void setThis();

    /**
     * @brief 协程无任务可调度时执行idle协程
     * 解决当协程调度器无任务，又不能让线程终止的情况
     */ 
    virtual void idle();

    /**
     * @brief 是否有空闲的线程
     * 
     * @return true 
     * @return false 
     */
    bool hasIdleThreads(){ return m_idleThreadCount>0;}
private:

    /**
     * @brief 协程调度启动(无锁)
     * @return 提示有任务来了
     */
    template <class FiberOrCb>
    bool scheduleNoLock(FiberOrCb fc, int thread) {
        bool need_tickle = m_fibers.empty();
        FiberAndThread ft(fc, thread);
        if (ft.fiber || ft.cb)
        {
            m_fibers.push_back(ft);
        }
        return need_tickle;
    }


public:
    /// 主协程，每个scheduler也需要一个协程负责任务
    static Fiber* GetMainFiber();
    static Scheduler* GetThis();

private:
    /// 任务：Fiber或者Call back
    struct FiberAndThread{
        Fiber::ptr fiber;
        std::function<void()> cb;
        int thread;   /// 有了线程id，就可以让协程在某个线程上执行

        FiberAndThread(Fiber::ptr f, int thr): fiber(f), thread(thr){}
        /// 用智能指针的指针来避免引用释放带来的问题
        FiberAndThread(Fiber::ptr *f, int thr):thread(thr){
            fiber.swap(*f);   // 交换内容
        }
        FiberAndThread(std::function<void()> f, int thr):cb(f), thread(thr){}
        FiberAndThread(std::function<void()> *f, int thr): thread(thr){
            cb.swap(*f);
        }
        FiberAndThread(): thread(-1){}
        void reset(){
            fiber = nullptr;
            cb = nullptr;
            thread = -1; // -1表示不指定任何线程
        } 
    };

private:

    MutexType m_mutex;
    /// 线程池
    std::vector<Thread::ptr> m_threads;   
    /// 待执行协程队列
    std::list<FiberAndThread> m_fibers;   
    /// 主协程
    Fiber::ptr m_rootFiber; 
    std::string m_name;

protected:

    std::vector<int> m_threadIds;   // 协程下的线程id数组
    size_t m_threadCount = 0;   // 线程数量
    std::atomic<size_t> m_activeThreadCount = {0};  // 工作线程数量
    std::atomic<size_t> m_idleThreadCount = {0};    // 空闲线程数量
    bool m_stopping = true; // 调度器状态，是否正在停止,true表示还没启动
    bool m_autoStop = false;    // 是否主动停止
    int m_rootThread = 0;   // 主线程id(use_caller)

};

}

#endif